// PPMtest.cpp : Defines the entry point for the console application.
//

#include "PPMtest.h"



////////////////////////////////////////////////////////////////////////////////////////
// configuration constants
////////////////////////////////////////////////////////////////////////////////////////
#define PPM_API			paMME// Audio API: use paWASAPI (Windows 7) or paWDMKS
#define PPM_SAMPLERATE	192000
#define PPM_MUTEOUT		1
#define PPM_DEBUGLOG	1	  	 // warning: huge files!



////////////////////////////////////////////////////////////////////////////////////////
// state variables
////////////////////////////////////////////////////////////////////////////////////////
t_ppm_state PPM;						// PPM library
	
JOYSTICK_POSITION joyState = { 0 };		// vJoy state
UINT iInterface = 1;					// Default target vJoy device

int Device_In  = -1;					// Audio devices
int Device_Out = -1;

#if PPM_DEBUGLOG
const int buf_len = PPM_SAMPLERATE;
volatile short buf_in[buf_len] = {0};
volatile short buf_v1[buf_len] = {0};
volatile int buf_wr = 0;
volatile int buf_rd = 0;
FILE *fout = NULL;
FILE *ftxt = NULL;
#endif

#if !PPM_MUTEOUT
short outs[6] = {0, 26, 51, 77, 102, 128};
#endif


////////////////////////////////////////////////////////////////////////////////////////
// function declaration
////////////////////////////////////////////////////////////////////////////////////////

static int audioCallback(const void *inputBuffer, 
						       void *outputBuffer,
                         unsigned long framesPerBuffer,
                         const PaStreamCallbackTimeInfo* timeInfo,
                         PaStreamCallbackFlags statusFlags,
                         void *userData);


void Process(void);
bool InitJoy(void);
void ProcessCmd(int argc, char* argv[]);


////////////////////////////////////////////////////////////////////////////////////////
// function definition
////////////////////////////////////////////////////////////////////////////////////////

int _tmain(int argc, _TCHAR* argv[])
{
	
	bool bOk = InitAudio();
	if (bOk)
	{
		ProcessCmd(argc, argv);

#if PPM_DEBUGLOG
		fopen_s(&fout, "datalog.pcm", "wb");
		fopen_s(&ftxt, "datalog.txt", "wt");
#endif

		ppm_reset(&PPM);

#if !PPM_MUTEOUT
		ppm_set_tx(&PPM, PPM_SAMPLERATE / 10000);
		ppm_set_values(&PPM, outs, 6);
#endif

		InitJoy();
		printf("\n");

		//PrintAPIInfo();

		bOk = CheckDevice(PPM_API, Device_In, Device_Out, PPM_SAMPLERATE);
		if (bOk)
		{
			printf("OK\n");

			bOk = OpenStream(PPM_API, Device_In, Device_Out, PPM_SAMPLERATE, audioCallback);
			if (bOk)
			{
				Process();
			}
		}
		else
		{
			printf("Format is NOT supported\n");
		}

		RelinquishVJD(iInterface);

#if PPM_DEBUGLOG
		fclose(fout);
		fclose(ftxt);
#endif
	}


	CloseAudio();


}



// This routine will be called by the PortAudio engine when audio is needed.
// It may be called at interrupt level on some machines so don't do anything
// that could mess up the system like calling malloc() or free().
////////////////////////////////////////////////////////////////////////////////////////
static int audioCallback(const void *inputBuffer, 
						       void *outputBuffer,
                         unsigned long framesPerBuffer,
                         const PaStreamCallbackTimeInfo* timeInfo,
                         PaStreamCallbackFlags statusFlags,
                         void *userData)
{
    TSAMPLE       *out = (TSAMPLE*)outputBuffer;
    const TSAMPLE *in  = (const TSAMPLE*)inputBuffer;
    
	// Prevent unused variable warnings
    (void) timeInfo; 
    (void) statusFlags;
    (void) userData;

	if (out != NULL)
	{
        for(unsigned int i = 0; i < framesPerBuffer; i++)
        {
#if PPM_MUTEOUT
			out[i] = 0;
#else
			out[i] = ppm_generate_sample(&PPM);
#endif
        }
    }

	if (in != NULL)
	{
		for(unsigned int i = 0; i < framesPerBuffer; i++)
        {
			ppm_process_sample(&PPM, in[i]);
			
#if PPM_DEBUGLOG
			buf_in[buf_wr] = in[i];
			buf_v1[buf_wr] = PPM.peak;
			
			buf_wr++;
			if(buf_wr >= buf_len)
			{
				buf_wr -= buf_len;
			}
#endif
        }
    }
    
    return paContinue;
}

// Input handling loop
////////////////////////////////////////////////////////////////////////////////////////
void Process(void)
{
	printf("\n");
	while (!_kbhit())
	{
		joyState.wAxisX = -ppm_get_value(&PPM, 1);
		joyState.wAxisY = -ppm_get_value(&PPM, 0);
		joyState.wAxisZ = ppm_get_value(&PPM, 4);
		joyState.wAxisXRot = -ppm_get_value(&PPM, 3);
		joyState.wAxisYRot = -ppm_get_value(&PPM, 5);
		joyState.wSlider = ppm_get_value(&PPM, 2);

		UpdateVJD(iInterface, (PVOID)&joyState);



		printf("                      \r%10s, ", ppm_has_framesync(&PPM) ? "SYNC" : "WAIT");
		for (int i = 0; i < ppm_get_rx_channels(&PPM); i++)
		{
			printf("%3d, ", ppm_get_value(&PPM, i));
		}

#if PPM_DEBUGLOG
		if (fout != NULL && ftxt != NULL)
		{

			while (buf_rd != buf_wr)
			{
				short data;

				data = buf_in[buf_rd];
				fwrite(&data, sizeof(data), 1, fout);

				data = buf_v1[buf_rd];
				fwrite(&data, sizeof(data), 1, fout);
				
				if (buf_v1[buf_rd] != 0)
				{
					fprintf(ftxt, "%d\n", buf_v1[buf_rd]);
				}
				buf_rd++;
				if (buf_rd >= buf_len)
				{
					buf_rd -= buf_len;
				}
			}
		}
#endif
		Sleep(10);
	}
	_getch();
	printf("\n");
}


// vJoy initialization
////////////////////////////////////////////////////////////////////////////////////////
bool InitJoy(void)
{

	printf("Initializing virtual joystick...\n");

	VjdStat status = GetVJDStatus(iInterface);
	switch (status)
	{
	case VJD_STAT_OWN:
		printf("vJoy Device %d is already owned by this feeder\n", iInterface);
		break;
	case VJD_STAT_FREE:
		printf("vJoy Device %d is free\n", iInterface);
		break;
	case VJD_STAT_BUSY:
		printf("vJoy Device %d is already owned by another feeder\nCannot continue\n", iInterface);
		return false;
	case VJD_STAT_MISS:
		printf("vJoy Device %d is not installed or disabled\nCannot continue\n", iInterface);
		return false;
	default:
		printf("vJoy Device %d general error\nCannot continue\n", iInterface);
		return false;
	};


	if (!vJoyEnabled())
	{
		printf("Failed Getting vJoy attributes.\n");
		return false;
	}
	else
	{
		printf("Vendor: %S\nProduct :%S\nVersion Number:%S\n",	GetvJoyManufacturerString(),
																GetvJoyProductString(),
																GetvJoySerialNumberString());
	}

	// Acquire the target
	if ((status == VJD_STAT_OWN) || ((status == VJD_STAT_FREE) && (!AcquireVJD(iInterface))))
	{
		printf("Failed to acquire vJoy device number %d.\n", iInterface);
		return false;
	}
	else
	{
		printf("Acquired: vJoy device number %d.\n", iInterface);
	}

	// Reset this device to default values
	ResetVJD(iInterface);




	return true;

}

// Command line parser
////////////////////////////////////////////////////////////////////////////////////////
void ProcessCmd(int argc, char* argv[])
{
	if (argc == 1)
	{
		printf("Audio device configuration\n-----------------------------\n");
		
		ListAudioDevices(PPM_API);
		
		printf("Select input device (-1 for default): ID = ");
		scanf("%d", &Device_In);
		printf("Select output device (-1 for default): ID = ");
		scanf("%d", &Device_Out);

		printf("\n\n");
	}
	else if (argc == 3)
	{
		sscanf(argv[1], "%d", &Device_In);
		sscanf(argv[2], "%d", &Device_Out);
	}
	else
	{
		printf("Syntax error!\nUse PPMtest.exe <Input Device ID> <Output Device ID>\n");
	}
}